import json
import dateutil.parser

from coin.exchange.base.kr_ws.ws_client_base import WebsocketUpdate
import coin.exchange.base.kr_ws.ws_parser_base as wspb
import coin.proto.coin_market_enums_pb2 as coin_enums
import coin.proto.coin_feed_pb2 as coin_feed

from coin.proto.coin_feed_pb2 import (BookEntry,
                                      BookMessage,
                                      TradeMessage,
                                      FeedMessage,
                                      FeedMessageBundle)
from coin.proto.coin_market_query_pb2 import (ProductTicker, ExchangeTicker)


class BithumbParser(object):
  @staticmethod
  def parse_ticker(json_msg: dict) -> ExchangeTicker:
    exchange_ticker = ExchangeTicker(exchange='Bithumb')
    ticker_list = []
    for currency, ticker_msg in json_msg['data'].items():
      ticker = ProductTicker()
      ticker.symbol = currency
      ticker.native_symbol = currency
      ticker.high = float(ticker_msg['max_price'])
      ticker.low = float(ticker_msg['min_price'])
      ticker.last = float(ticker_msg['closing_price'])
      ticker.bid = float(ticker_msg['buy_price'])
      ticker.ask = float(ticker_msg['sell_price'])
      ticker.volume_24hr = float(ticker_msg['units_traded'])
      ticker_list.append(ticker)
    exchange_ticker.each_ticker.extend(ticker_list)
    return exchange_ticker

  @staticmethod
  def parse_book_with_time(fetched_time: int, json_msg: dict) -> ExchangeTicker:
    orderbook_msg = json_msg['data']

    ask_list = []
    bid_list = []
    for ask in orderbook_msg['asks']:
      book_entry = BookEntry()
      book_entry.price = int(ask['price'])
      book_entry.qty_float = float(ask['quantity'])
      ask_list.append(book_entry)
    for bid in orderbook_msg['bids']:
      book_entry = BookEntry()
      book_entry.price = int(bid['price'])
      book_entry.qty_float = float(bid['quantity'])
      bid_list.append(book_entry)

    bid_list = sorted(bid_list, key=lambda elem: -elem.price)
    ask_list = sorted(ask_list, key=lambda elem: elem.price)
    book_message = BookMessage(ask=ask_list, bid=bid_list)
    return FeedMessage(
        feeder_id=0,
        machine_id=0,
        symbol=json_msg['header']['currency'],
        exchange_type=coin_enums.Bithumb,
        fetched_time=fetched_time,
        feed_type=coin_feed.BOOK_FEED,
        book=book_message,
    )

  @staticmethod
  def parse_book(update: WebsocketUpdate, json_msg: dict) -> ExchangeTicker:
    return BithumbParser.parse_book_with_time(int(update.timestamp * 10**9), json_msg)

  @staticmethod
  def parse_one_trade(trade_msg: dict, fetched_time: int, symbol: str) -> FeedMessage:
    trade_feed = TradeMessage(price=int(trade_msg['price']),
                              qty_float=float(trade_msg['units_traded']))
    if trade_msg['type'] == 'ask':
      trade_feed.side = coin_feed.TRADE_SELL_SIDE
    elif trade_msg['type'] == 'bid':
      trade_feed.side = coin_feed.TRADE_BUY_SIDE
    elif trade_msg['type'] == 'dn':
      trade_feed.side = coin_feed.TRADE_NO_SIDE
    elif trade_msg['type'] == 'up':
      trade_feed.side = coin_feed.TRADE_NO_SIDE
    else:
      raise ValueError('Unexpected trade_side value:\n' + json.dumps(trade_msg, indent=2))

    exchange_time = int(dateutil.parser.parse(trade_msg['transaction_date']).timestamp()
                        * 10**9)  # Need check!!
    return FeedMessage(
        feeder_id=0,
        machine_id=0,
        symbol=symbol,
        exchange_type=coin_enums.Bithumb,
        exchange_time=exchange_time,
        fetched_time=fetched_time,
        feed_type=coin_feed.TRADE_FEED,
        trade=trade_feed,
    )

  @staticmethod
  def parse_trade(update: WebsocketUpdate, json_msg: dict) -> FeedMessageBundle:
    symbol = json_msg['header']['currency']
    fetched_time = int(update.timestamp * 10**9)
    feed_messages = [
        BithumbParser.parse_one_trade(trade_msg, fetched_time, symbol)
        for trade_msg in json_msg['data']
    ]
    return FeedMessageBundle(feed=feed_messages)


class BithumbWSParserManager(wspb.WebsocketParserManagerBase):
  def __init__(self, handler):
    super().__init__(handler=handler)

  def on_sent_msg(self, update):
    msg = json.loads(update.msg)

  def on_recv_msg(self, update):
    if update.msg == "":
      return

    try:
      json_msg = json.loads(update.msg)
    except ValueError:
      print('Invalid json: "%s"' % update.msg)
      return

    if json_msg["header"]["service"] == "transaction":
      pb = BithumbParser.parse_trade(update, json_msg)
      self._handler.on_trade(pb)
    elif json_msg["header"]["service"] == "orderbook":
      pb = BithumbParser.parse_book(update, json_msg)
      self._handler.on_book(pb)
    elif json_msg["header"]["service"] == "ticker":
      # pb = BithumbParser.parse_ticker(json_msg)
      # self._handler.on_ticker(pb)
      pass
    else:
      # raise ValueError(str(update))
      pass
